package com.innovation.client;

import java.lang.reflect.Method;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import com.innovation.message.ClientActionProto.ClientAction;
import com.innovation.message.ErrorMsgProto.ErrorMsg;
import com.innovation.message.PBMessageProto.PBMessage;

public class UserClient extends WebSocketClient{
	

	private static Logger logger = Logger.getLogger(UserClient.class);
	private static final Queue<NotifyItem<?>> waitQueue = new LinkedList<NotifyItem<?>>();//等待执行的数据项
	private static final Map<Integer, NotifyItem<?>> taskMap = new ConcurrentHashMap<Integer, NotifyItem<?>>(); //正在运行中的数据项
	
	private int playerId = 0;
	private String openId = "";
	private String name = "";
	private int seq = 0;
	public static final String LOCAL = "ws://192.168.1.127:30000/websocket";
	public static final String SERVER = "ws://58.61.152.91:17000/websocket";
	
	public UserClient(String openId, String name, String url) throws Exception{
		this(new URI(url));
		this.openId = openId;
		this.name = name;
		
		//连接服务器端
		connectBlocking();
		
		//登录
		login();
	}
	
	public UserClient(URI serverURI) {
		super(serverURI);
	}

	@Override
	public void onOpen(ServerHandshake handshakedata) {
		logger.info("websocket opened!");
		NotifyItem<?> item = waitQueue.poll();
		while(item != null){
			taskMap.put(item.seq, item);
			send(item.data);
			
			try {
				item.wait(3000);
			} catch (InterruptedException e) {
			}
		}
	}

	@Override
	public void onMessage(String message) {
		
	}

	@Override
	public void onClose(int code, String reason, boolean remote) {
		logger.info("websocket closed!"+reason);
	}

	@Override
	public void onError(Exception ex) {
		logger.info("websocket exception!");
		ex.printStackTrace();
	}
	
	@Override
	public void onMessage(ByteBuffer bytes) {
		try {
			PBMessage response = PBMessage.parseFrom(bytes.array());
			int sequence = response.getSequence();
			Class<?> clazz = null;
			NotifyItem<?> item = null;
			if(sequence > 0){
				item = taskMap.get(sequence);
				System.out.println("net use time:"+(System.currentTimeMillis() - item.sendTime)+" code="+item.code);
				clazz = item.clazz;
			}
			if(response.getErrorCode() > 0){
				clazz = ErrorMsg.class;
			}
//			//PVP开始战斗,服务器端推送
//			if(response.getCode() == ClientAction.ACTION_PVP_START_VALUE){
//				clazz = PVPDataMsg.class;
//			}
			Message message = null;
			if(response.hasData() && clazz != null){
				Method method = clazz.getMethod("parseFrom", new Class[]{ByteString.class});
				message = (Message)method.invoke(clazz, response.getData());
			}
			if(response.getCode() == ClientAction.ACTION_LOGIN_VALUE){
//				LoginResp loginMsg = (LoginResp)message;
//				this.playerId = loginMsg.getPlayer().getId();
//			}else{
				if(message != null){
					System.out.println(message);
				}
				if(response.hasDataMsg()){
					System.out.println("DataMsg data:\n"+response.getDataMsg());
				}
			}
			if(item != null){
				synchronized (item) {
					item.notify();
				}
				
				taskMap.remove(sequence);
			}
		} catch (Exception e) {
		}
	}
	
	public <T extends Message> void send(int code, Message msg, Class<T> clazz){
		PBMessage.Builder request = PBMessage.newBuilder();
		request.setSequence(++seq);
		request.setPlayerId(this.playerId);
		request.setCode(code);
		if(msg != null){
			request.setData(msg.toByteString());
		}
		
		NotifyItem<T> item = new NotifyItem<T>();
		item.seq = seq;
		item.data = request.build().toByteArray();
		item.clazz = clazz;
		item.sendTime = System.currentTimeMillis();
		item.code = code;
		
		if(getConnection().isOpen()){
			taskMap.put(item.seq, item);
			send(item.data);
			
			try {
				synchronized (item) {
					item.wait(10000);
				}
			} catch (InterruptedException e) {
			}
		}else{
			waitQueue.offer(item);
		}
	}
	
	/**
	 * 判断是否执行完毕
	 * @return
	 */
	public boolean isFinished(){
		return (waitQueue.size() == 0 && taskMap.size() == 0);
	}
	
	/**
	 * 登录
	 */
	public void login(){
//		LoginReq.Builder builder = LoginReq.newBuilder();
//		builder.setOpenId(this.openId);
//		builder.setName(this.name);
//		
//		send(ClientAction.ACTION_LOGIN_VALUE, builder.build(), LoginResp.class);
	}
	
	public int getPlayerId(){
		return this.playerId;
	}
	
	private static class NotifyItem<T>{
		public int seq;
		public byte[] data;
		public Class<T> clazz;
		public long sendTime;
		public int code;
	}
}
